<template>
  <div class="container">
    <!-- <h1>智算中心拓扑设计</h1> -->

    <!-- <div class="controls">
      <button @click="resetCamera">重置视角</button>
      <button @click="toggleConnections">{{ showConnections ? '隐藏连接' : '显示连接' }}</button>
      <button @click="toggleLabels">{{ showLabels ? '隐藏标签' : '显示标签' }}</button>
      <button @click="toggleGrid">{{ showGrid ? '隐藏网格' : '显示网格' }}</button>
      <button @click="togglePodView">{{ showPodView ? '整体视图' : 'Pod视图' }}</button>
    </div> -->

    <div class="stats">
      <div>总算力 (FP16): 2,000P</div>
      <div>算力设备: {{ SERVER_COUNT }}台</div>
      <div>算力卡: {{ totalGPUs }}张</div>
      <div>网络设备: 48台</div>
      <div>链路带宽: 400Gbps</div>
      <div>总连接线缆: 3072条</div>
    </div>

    <div class="legend">
      <div><span class="server-legend"></span> GPU服务器</div>
      <div><span class="leaf-legend"></span> Leaf交换机</div>
      <div><span class="spine-legend"></span> Spine交换机</div>
      <div><span class="pod-legend"></span> Pod边界</div>
      <div><span class="connection-legend"></span> 网络连接</div>
    </div>

    <div ref="threeContainer" class="three-container"></div>

    <div v-if="hoveredObject" class="tooltip" :style="tooltipStyle">
      <h3>{{ hoveredObject.name }}</h3>
      <p v-if="hoveredObject.type === 'server'">
        型号: H200 8卡服务器<br>
        GPU: 8个NVIDIA H200<br>
        网卡: 8个400G网卡<br>
        所属Pod: Pod-{{ Math.floor(hoveredObject.index/32)+1 }}<br>
        连接Leaf: Pod内全部8台Leaf
      </p>
      <p v-if="hoveredObject.type === 'leaf'">
        型号: QM9790<br>
        端口: 64×400G (16下联+32上联+16备用)<br>
        所属Pod: Pod-{{ Math.floor(hoveredObject.index/8)+1 }}<br>
        下联服务器: 16台 (每台1个连接)<br>
        上联Spine: 16台 (每台2条)
      </p>
      <p v-if="hoveredObject.type === 'spine'">
        型号: QM9790<br>
        端口: 64×400G<br>
        连接Leaf: 32条 (来自16台Leaf)
      </p>
      <p v-if="hoveredObject.type === 'pod'">
        Pod-{{ hoveredObject.index+1 }}<br>
        包含: 32台H200服务器<br>
        包含: 8台Leaf交换机<br>
        总GPU: 256个<br>
        服务器-Leaf连接: 全连接(每服务器8条)
      </p>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'

// 设备数量配置
const SERVER_COUNT = 128
const LEAF_COUNT = 32
const SPINE_COUNT = 16
const POD_COUNT = 4 // 128/32=4个Pod

// 连接配置
const SERVERS_PER_POD = 32
const LEAFS_PER_POD = 8
const UPLINKS_PER_LEAF = 32 // 每个Leaf上联32口
const SPINES_PER_LEAF_LINK = 2 // 每个Leaf连接到每个Spine的链路数
const CONNECTIONS_PER_SERVER = 8 // 每服务器8个网卡连接

// 引用和状态
const threeContainer = ref(null)
const showConnections = ref(true)
const showLabels = ref(false)
const showGrid = ref(true)
const showPodView = ref(false)
const hoveredObject = ref(null)
const tooltipStyle = ref({ left: '0px', top: '0px' })

// Three.js 相关变量
let scene, camera, renderer, labelRenderer, controls, raycaster, mouse
let spines = [], leaves = [], servers = [], connections = [], grid = null, pods = []

// 计算属性
const totalDevices = computed(() => SPINE_COUNT + LEAF_COUNT + SERVER_COUNT)
const totalConnections = computed(() => SERVER_COUNT * CONNECTIONS_PER_SERVER + LEAF_COUNT * SPINE_COUNT * SPINES_PER_LEAF_LINK)
const totalBandwidth = computed(() => {
  const serverLeaf = SERVER_COUNT * CONNECTIONS_PER_SERVER * 400 // 400G per NIC
  const leafSpine = LEAF_COUNT * SPINE_COUNT * SPINES_PER_LEAF_LINK * 400 // 400G per link
  return `${(serverLeaf + leafSpine) / 1000}Tbps`
})
const totalGPUs = computed(() => SERVER_COUNT * 8)

const autoRotate = ref(true)
const rotationSpeed = 0.8 // 旋转速度，可调整

// 初始化场景
function initThreeJS() {
  // 创建场景
  scene = new THREE.Scene()
  scene.background = null

  // 添加环境光和方向光
  const ambientLight = new THREE.AmbientLight(0x404040)
  scene.add(ambientLight)

  const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
  directionalLight.position.set(1, 1, 1)
  scene.add(directionalLight)

  // 创建相机
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
  camera.position.set(0, 50, 100)

  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
  renderer.setClearColor(0x000000, 0) // 完全透明
  renderer.setSize(threeContainer.value.clientWidth, threeContainer.value.clientHeight)
  threeContainer.value.appendChild(renderer.domElement)

  // 创建标签渲染器
  labelRenderer = new CSS2DRenderer()
  labelRenderer.setSize(threeContainer.value.clientWidth, threeContainer.value.clientHeight)
  labelRenderer.domElement.style.position = 'absolute'
  labelRenderer.domElement.style.top = '0'
  threeContainer.value.appendChild(labelRenderer.domElement)

  // 添加轨道控制器
  controls = new OrbitControls(camera, labelRenderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05

  // 初始化射线检测
  raycaster = new THREE.Raycaster()
  mouse = new THREE.Vector2()

  // 创建网格
  createGrid()

  // 创建设备
  createDevices()

  // 添加窗口大小调整事件
  window.addEventListener('resize', onWindowResize)
  threeContainer.value.addEventListener('mousemove', onMouseMove)

  // 更新轨道控制器配置
  controls = new OrbitControls(camera, labelRenderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05
  controls.autoRotate = autoRotate.value // 初始状态
  controls.autoRotateSpeed = rotationSpeed // 设置旋转速度

}

// 新增自动旋转切换函数
function toggleAutoRotate() {
  autoRotate.value = !autoRotate.value
  controls.autoRotate = autoRotate.value

  // 当启用自动旋转时，重置目标点为场景中心
  if (autoRotate.value) {
    controls.target.set(0, 0, 0)
  }
}

// 修改动画循环函数
function animate() {
  requestAnimationFrame(animate)

  // 更新控制器
  controls.update()

  // 只在需要时更新渲染（优化性能）
  if (controls.autoRotate || controls.enableDamping) {
    renderer.render(scene, camera)
    labelRenderer.render(scene, camera)
  }
}


// 创建网格
function createGrid() {
  if (grid) scene.remove(grid)

  if (!showGrid.value) return

  const size = 140
  const divisions = 14
  const gridHelper = new THREE.GridHelper(size, divisions, 0x495670, 0x303a4d)
  gridHelper.position.y = -0.5
  scene.add(gridHelper)
  grid = gridHelper
}

// 创建设备
function createDevices() {
  // ...清除现有设备的代码保持不变...

  // Spine交换机布局 - 中心单行排列
  for (let i = 0; i < SPINE_COUNT; i++) {
    const x = (i - (SPINE_COUNT - 1) / 2) * 10
    const z = 0
    const y = 25

    const spine = createSwitch(x, y, z, 0x1a7f37, `Spine-${i+1}`, 'spine')
    spines.push(spine)
  }

  // 创建Pod (4个Pod)
  for (let podIndex = 0; podIndex < POD_COUNT; podIndex++) {
    const podX = (podIndex % 2) * 80 - 40
    const podZ = Math.floor(podIndex / 2) * 80 - 40

    // 创建Pod边界
    const podGeometry = new THREE.BoxGeometry(70, 0.1, 70)
    const podMaterial = new THREE.MeshBasicMaterial({
      color: 0x4444ff,
      transparent: true,
      opacity: 0.1,
      side: THREE.DoubleSide
    })
    const pod = new THREE.Mesh(podGeometry, podMaterial)
    pod.position.set(podX, 0, podZ)
    pod.userData = { name: `Pod-${podIndex+1}`, type: 'pod', index: podIndex }
    scene.add(pod)
    pods.push(pod)

    // Pod标签
    const podLabel = createLabel(`Pod-${podIndex+1}`, 0xffffff)
    podLabel.position.set(podX, 1, podZ)
    scene.add(podLabel)

    // Leaf交换机布局 (每个Pod 8台Leaf，与服务器列对齐)
    const leafSpacing = 7
    for (let col = 0; col < 8; col++) {
      const globalLeafIndex = podIndex * LEAFS_PER_POD + col
      const x = podX + (col - 3.5) * leafSpacing
      const z = podZ
      const y = 15

      const leaf = createSwitch(x, y, z, 0xff7f50, `Leaf-${globalLeafIndex+1}`, 'leaf')
      leaf.userData.podIndex = podIndex
      leaf.userData.column = col // 记录所属列
      leaves.push(leaf)
    }

    // 服务器布局 (每个Pod 32台，4行8列，每列对齐一个Leaf)
    const serverSpacingX = 7 // 与Leaf间距一致
    const serverSpacingZ = 5
    for (let row = 0; row < 4; row++) {
      for (let col = 0; col < 8; col++) {
        const serverIndexInPod = row * 8 + col
        const globalServerIndex = podIndex * SERVERS_PER_POD + serverIndexInPod
        const x = podX + (col - 3.5) * serverSpacingX // 与Leaf x坐标相同
        const z = podZ + (row - 1.5) * serverSpacingZ
        const y = 0

        const server = createServer(x, y, z, 0x8892b0, `GPU-${globalServerIndex+1}`, 'server')
        server.userData.podIndex = podIndex
        server.userData.column = col // 记录所属列
        servers.push(server)
      }
    }
  }

  // 创建连接
  createConnections()
}

// 创建交换机
function createSwitch(x, y, z, color, name, type) {
  const geometry = new THREE.BoxGeometry(3, 2, 1.5)
  const material = new THREE.MeshPhongMaterial({
    color,
    emissive: 0x000000,
    emissiveIntensity: 0
  })
  const mesh = new THREE.Mesh(geometry, material)
  mesh.position.set(x, y, z)
  mesh.userData = { name, type }
  scene.add(mesh)

  // 添加标签
  if (showLabels.value) {
    const label = createLabel(name, 0xffffff)
    label.position.set(x, y + 2, z)
    scene.add(label)
    mesh.userData.label = label
  }

  return mesh
}

// 创建服务器
function createServer(x, y, z, color, name, type) {
  const geometry = new THREE.BoxGeometry(1.8, 1.2, 1.8) // 稍大的服务器模型
  const material = new THREE.MeshPhongMaterial({
    color,
    emissive: 0x000000,
    emissiveIntensity: 0
  })
  const mesh = new THREE.Mesh(geometry, material)
  mesh.position.set(x, y, z)
  mesh.userData = { name, type }
  scene.add(mesh)

  // 添加标签
  if (showLabels.value) {
    const label = createLabel(name, 0xffffff)
    label.position.set(x, y + 1.8, z)
    scene.add(label)
    mesh.userData.label = label
  }

  return mesh
}

// 创建标签
function createLabel(text, color) {
  const labelDiv = document.createElement('div')
  labelDiv.className = 'three-label'
  labelDiv.textContent = text
  labelDiv.style.color = `rgb(${color >> 16}, ${(color >> 8) & 0xff}, ${color & 0xff})`

  const label = new CSS2DObject(labelDiv)
  label.layers.set(0)
  return label
}

// 在createConnections函数中更新Leaf-Spine连接逻辑
function createConnections() {
  // 清除现有连接
  connections.forEach(conn => scene.remove(conn))
  connections = []

  if (!showConnections.value) return

  // Leaf-Spine连接 (每台Leaf 32上联端口 = 16 Spine × 2)
  leaves.forEach((leaf, lIndex) => {
    // 计算该Leaf需要连接的Spine
    const connectionsPerSpine = 2 // 每台Spine连接2条
    const totalSpines = SPINE_COUNT

    // 均匀分配到所有Spine
    for (let i = 0; i < totalSpines; i++) {
      for (let j = 0; j < connectionsPerSpine; j++) {
        const spineIndex = (i + lIndex) % totalSpines // 均匀分布
        createConnection(
          leaf.position,
          spines[spineIndex].position,
          // 0x64ffda,
          0x4da6ee, // 蓝色
          0.5,
          true, // 是上行链路
          j * 0.3 - 0.15 // 轻微偏移
        )
      }
    }
  })

  // Server-Leaf连接保持不变
  servers.forEach((server, sIndex) => {
    const podIndex = Math.floor(sIndex / SERVERS_PER_POD)
    const podLeaves = leaves.slice(
      podIndex * LEAFS_PER_POD,
      (podIndex + 1) * LEAFS_PER_POD
    )

    // 每个服务器连接到Pod内全部8台Leaf
    podLeaves.forEach((leaf, i) => {
      createConnection(
        server.position,
        leaf.position,
        0x64ffda, // 青色
        0.6,
        false, // 是下行链路
        i * 0.2 - 0.7 // 分散连接
      )
    })
  })
}

// 更新createConnection函数增加连接线类型区分
function createConnection(startPos, endPos, color, opacity, isUplink, offset = 0) {
  // 上行连接使用更明显的曲线
  const curveHeight = isUplink ? 5 : 5

  // 计算中间控制点
  const controlPoint = new THREE.Vector3(
    (startPos.x + endPos.x) / 2 + offset,
    (startPos.y + endPos.y) / 2 + curveHeight,
    (startPos.z + endPos.z) / 2
  )

  // 创建二次贝塞尔曲线
  const curve = new THREE.QuadraticBezierCurve3(
    new THREE.Vector3(
      startPos.x + offset,
      startPos.y,
      startPos.z
    ),
    controlPoint,
    new THREE.Vector3(
      endPos.x + offset,
      endPos.y,
      endPos.z
    )
  )

  const points = curve.getPoints(10) // 更多点使曲线更平滑
  const geometry = new THREE.BufferGeometry().setFromPoints(points)
  const material = new THREE.LineBasicMaterial({
    color,
    transparent: true,
    opacity: isUplink ? opacity * 0.8 : opacity, // 上行链路更透明
    linewidth: isUplink ? 1.5 : 1
  })

  const line = new THREE.Line(geometry, material)
  scene.add(line)
  connections.push(line)
}

// 鼠标移动事件
function onMouseMove(event) {
  // 计算鼠标位置
  const rect = threeContainer.value.getBoundingClientRect()
  mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1
  mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1

  // 射线检测
  raycaster.setFromCamera(mouse, camera)
  const intersects = raycaster.intersectObjects([...spines, ...leaves, ...servers, ...pods])

  // 重置之前高亮的对象
  if (hoveredObject.value) {
    const prevObj = [...spines, ...leaves, ...servers, ...pods].find(
      obj => obj.userData.name === hoveredObject.value.name
    )
    if (prevObj) prevObj.material.emissiveIntensity = 0
  }

  // 处理新的悬停对象
  if (intersects.length > 0) {
    const obj = intersects[0].object
    obj.material.emissiveIntensity = 0.5

    hoveredObject.value = {
      name: obj.userData.name,
      type: obj.userData.type,
      index: obj.userData.index,
      podIndex: obj.userData.podIndex
    }

    tooltipStyle.value = {
      left: `${event.clientX + 15}px`,
      top: `${event.clientY + 15}px`
    }

    // 如果是Pod视图模式，聚焦当前Pod
    if (showPodView.value && obj.userData.podIndex !== undefined) {
      focusPod(obj.userData.podIndex)
    }
  } else {
    hoveredObject.value = null
  }
}

// 聚焦Pod
function focusPod(podIndex) {
  const pod = pods[podIndex]
  if (!pod) return

  // 计算Pod中心位置
  const targetPosition = pod.position.clone()
  targetPosition.y += 20

  // 动画移动相机
  animateCameraTo(targetPosition)
}

// 动画移动相机
function animateCameraTo(targetPosition) {
  const duration = 1000 // 1秒
  const startPosition = camera.position.clone()
  const startTime = Date.now()

  function updateCamera() {
    const elapsed = Date.now() - startTime
    const progress = Math.min(elapsed / duration, 1)

    camera.position.lerpVectors(startPosition, targetPosition, progress)
    controls.target.lerpVectors(controls.target, targetPosition, progress)

    if (progress < 1) {
      requestAnimationFrame(updateCamera)
    }
  }

  updateCamera()
}

// 切换Pod视图
function togglePodView() {
  showPodView.value = !showPodView.value

  if (!showPodView.value) {
    // 返回整体视图
    resetCamera()
  }
}

// 窗口大小调整
function onWindowResize() {
  const width = threeContainer.value.clientWidth
  const height = threeContainer.value.clientHeight

  camera.aspect = width / height
  camera.updateProjectionMatrix()

  renderer.setSize(width, height)
  labelRenderer.setSize(width, height)
}

// 重置相机
function resetCamera() {
  camera.position.set(0, 50, 100)
  controls.target.set(0, 0, 0)
  controls.update()
}

// 切换连接显示
function toggleConnections() {
  showConnections.value = !showConnections.value
  createConnections()
}

// 切换标签显示
function toggleLabels() {
  showLabels.value = !showLabels.value

  const allObjects = [...spines, ...leaves, ...servers, ...pods]
  allObjects.forEach(obj => {
    if (obj.userData.label) {
      if (showLabels.value) {
        scene.add(obj.userData.label)
      } else {
        scene.remove(obj.userData.label)
      }
    }
  })
}

// 切换网格显示
function toggleGrid() {
  showGrid.value = !showGrid.value
  createGrid()
}

// 动画循环
// function animate() {
//   requestAnimationFrame(animate)
//   controls.update()
//   renderer.render(scene, camera)
//   labelRenderer.render(scene, camera)
// }

// 生命周期钩子
onMounted(() => {
  initThreeJS()
  animate()
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', onWindowResize)
  threeContainer.value.removeEventListener('mousemove', onMouseMove)
})
</script>

<style scoped>
/* 样式部分保持不变 */
.container {
  width: 100%;
  height: 100vh;
  background: transparent;
  color: #e6f1ff;
  padding: 20px;
  font-family: 'Arial', sans-serif;
  overflow: hidden;
  position: relative;
}

h1, h2 {
  text-align: center;
  margin-bottom: 10px;
  color: #64ffda;
}

h2 {
  font-size: 1.2em;
  color: #8892b0;
  margin-bottom: 20px;
}

.controls {
  display: flex;
  justify-content: center;
  gap: 15px;
  margin-bottom: 20px;
}

.controls button {
  background: #112240;
  color: #64ffda;
  border: 1px solid #64ffda;
  border-radius: 4px;
  padding: 8px 15px;
  cursor: pointer;
  transition: all 0.3s;
}

.controls button:hover {
  background: #64ffda;
  color: #0a192f;
}

.legend {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 15px;
  margin-bottom: 20px;
}

.legend div {
  display: flex;
  align-items: center;
  font-size: 14px;
}

.server-legend, .leaf-legend, .spine-legend, .pod-legend, .connection-legend {
  display: inline-block;
  width: 15px;
  height: 15px;
  margin-right: 5px;
  border-radius: 2px;
}

.server-legend {
  background: #8892b0;
}

.leaf-legend {
  background: #ff7f50;
}

.spine-legend {
  background: #1a7f37;
}

.pod-legend {
  background: #4444ff;
}

.connection-legend {
  background: #4da6ff;
}

.three-container {
  width: 100%;
  height: 80vh;
  position: relative;
}

.stats {
  display: flex;
  justify-content: center;
  gap: 30px;
  margin-top: 20px;
  font-size: 16px;
  margin-bottom: 20px;
}

.stats div {
  background: #112240;
  padding: 10px 20px;
  border-radius: 5px;
  border: 1px solid #64ffda;
}

.tooltip {
  position: fixed;
  background: #112240;
  border: 1px solid #64ffda;
  border-radius: 5px;
  padding: 15px;
  max-width: 300px;
  z-index: 100;
  pointer-events: none;
}

.tooltip h3 {
  color: #64ffda;
  margin-top: 0;
  margin-bottom: 10px;
}

.tooltip p {
  margin: 5px 0;
  font-size: 14px;
  line-height: 1.4;
}

.three-label {
  font-size: 12px;
  font-weight: bold;
  pointer-events: none;
  text-shadow: 0 0 5px #000;
  white-space: nowrap;
}
</style>
